// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.

#include <stdarg.h>
#include <stdint.h>
#include "dw_uart.h"
#include "reg_common.h"
#include "scu.h"

#define CPU_CFG_REG_BASE_ADDR (0x110000000 + 0x4000)
//#define CPU_CFG_REG_BASE_ADDR (0x110000000)

#define REG_SUBSYS_GEN_IRQ    0xFF028000
#define REG_SUBSYS_GEN_IRQ_EN 0xFF028008

// #define REGBLK_SUBSYS_UART_ADDR 0x10000000
#define REGBLK_SUBSYS_UART_ADDR 0xFF027000
#define UART_DATA_ADDR  (REGBLK_SUBSYS_UART_ADDR + 0x0)
#define UART_STATE_ADDR (REGBLK_SUBSYS_UART_ADDR + 0x4)
#define UART_CTRL_ADDR  (REGBLK_SUBSYS_UART_ADDR + 0x8)
#define UART_INT_ADDR   (REGBLK_SUBSYS_UART_ADDR + 0xC)
#define UART_BAUD_ADDR  (REGBLK_SUBSYS_UART_ADDR + 0x10)
#define UART_PID4_ADDR  (REGBLK_SUBSYS_UART_ADDR + 0xFD0) #0x04
#define UART_BAUT_COE   (12000000 / 115200)

#define REGBLK_SUBSYS_PLIC_ADDR 0x114000000
#define PLIC_PRI_BASE_ADDR      (REGBLK_SUBSYS_PLIC_ADDR + 0x0)
#define PLIC_PRI_SRC1           (REGBLK_SUBSYS_PLIC_ADDR + 0x4)
#define PLIC_PRI_SRC2           (REGBLK_SUBSYS_PLIC_ADDR + 0x8)
#define PLIC_PRI_SRC31          (REGBLK_SUBSYS_PLIC_ADDR + 0x7c)

#define PLIC_IP                 (REGBLK_SUBSYS_PLIC_ADDR + 0x1000)
#define PLIC_IE                 (REGBLK_SUBSYS_PLIC_ADDR + 0x2000)
#define PLIC_PRI_THRES          (REGBLK_SUBSYS_PLIC_ADDR + 0x200000)
#define PLIC_CLAIM              (REGBLK_SUBSYS_PLIC_ADDR + 0x200004)

#define USE_MYSTDLIB

extern long time();
extern long insn();

#ifdef USE_MYSTDLIB
extern char *malloc();
extern int printf(const char *format, ...);

extern void *memcpy(void *dest, const void *src, long n);
extern char *strcpy(char *dest, const char *src);
extern int strcmp(const char *s1, const char *s2);

char heap_memory[1024];
int heap_memory_used = 0;

#else

extern int printf(const char *format, ...);

#endif

long time()
{
	int cycles;
	asm volatile ("rdcycle %0" : "=r"(cycles));
	// printf("[time() -> %d]", cycles);
	return cycles;
}

long insn()
{
	int insns;
	asm volatile ("rdinstret %0" : "=r"(insns));
	// printf("[insn() -> %d]", insns);
	return insns;
}

void delay(uint64_t value)
{
	while(value--);
}

void nc_init(void)
{
	// set nc for perip
	// // write32(CPU_CFG_REG_BASE_ADDR + 0x210, 0xff027000);
	// write32(CPU_CFG_REG_BASE_ADDR + 0x210, 0xD0000000);
	// //write32(CPU_CFG_REG_BASE_ADDR + 0x210, 0x00000000);
	// write32(CPU_CFG_REG_BASE_ADDR + 0x214, 0x0);
	// write32(CPU_CFG_REG_BASE_ADDR + 0x218, 0xffffffff);
	// write32(CPU_CFG_REG_BASE_ADDR + 0x21c, 0x0);

	 //set nc for core perip(plic/clint/timer)
	/*
	write32(CPU_CFG_REG_BASE_ADDR + 0x220, 0x14000000);
	 write32(CPU_CFG_REG_BASE_ADDR + 0x224, 0x01);
	 write32(CPU_CFG_REG_BASE_ADDR + 0x228, 0x18104000);
	 write32(CPU_CFG_REG_BASE_ADDR + 0x22c, 0x01);

	write32(CPU_CFG_REG_BASE_ADDR + 0x230, 0xD8000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x234, 0x0);
	write32(CPU_CFG_REG_BASE_ADDR + 0x238, 0xffffffff);
	write32(CPU_CFG_REG_BASE_ADDR + 0x23c, 0x0);
*/

	write32(CPU_CFG_REG_BASE_ADDR + 0x220, 0x00000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x224, 0x01);
	write32(CPU_CFG_REG_BASE_ADDR + 0x228, 0x00000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x22c, 0x40);

	write32(CPU_CFG_REG_BASE_ADDR + 0x260, 0x00000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x264, 0x41);
	write32(CPU_CFG_REG_BASE_ADDR + 0x268, 0x00000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x26c, 0x80);

	write32(CPU_CFG_REG_BASE_ADDR + 0x250, 0x00000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x254, 0x81);
	write32(CPU_CFG_REG_BASE_ADDR + 0x258, 0x00000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x25c, 0x100);

	write32(CPU_CFG_REG_BASE_ADDR + 0x230, 0x00000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x234, 0x0);
	write32(CPU_CFG_REG_BASE_ADDR + 0x238, 0xD8000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x23c, 0x0);

	write32(CPU_CFG_REG_BASE_ADDR + 0x240, 0xE0000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x244, 0x0);
	write32(CPU_CFG_REG_BASE_ADDR + 0x248, 0xF0040000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x24c, 0x0);
	__asm__ volatile ("fence.i");
}

void ddr_pcie_cache_en(void)
{
	write32(CPU_CFG_REG_BASE_ADDR + 0x220, 0x00000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x224, 0x01);
	write32(CPU_CFG_REG_BASE_ADDR + 0x228, 0x00000000);
	write32(CPU_CFG_REG_BASE_ADDR + 0x22c, 0x40);
	__asm__ volatile ("fence.i");
	uint32_t nc_region0[4] = {0};
	for(uint8_t i = 0; i < 4; i++)
	{
		nc_region0[i] = read32(CPU_CFG_REG_BASE_ADDR + 0x220 + i * 4);
		printf("[ACPU] NC Region0[%i] Addr[0x%08lx] Get[0x%04x].\r\n", i, CPU_CFG_REG_BASE_ADDR + 0x220 + i * 4, nc_region0[i]);
	}
	printf("[ACPU] DDR PCIE Memory space cache enable.\r\n");
}

void plic_init()
{
	uint8_t i = 0;
	// plic_init
	// set plic ip
	// set plic ie-> enable bit[1] for uart intr
	write32(PLIC_IE + 0x0, 0xffffffff8);
	write32(PLIC_IE + 0x4, 0xfffffffff);
	// set plic pri thres
	write32(PLIC_PRI_THRES, 0x0);
	// set plic pri srcl and src2
	for(i = 1; i < 64; i++){
		write32(PLIC_PRI_BASE_ADDR + (i* 4), 0x02);
	}
	write32(REG_SUBSYS_GEN_IRQ_EN, 0x1);
}

void plic_test()
{
	uint8_t i = 3;
	for(; i < 64; i++)
	{
		__asm__ volatile ("fence.i");
		write64(REG_SUBSYS_GEN_IRQ, (uint64_t)0x1 << i);
		// delay(10000);
	}
}

void sys_init()
{
	nc_init();
	// write32(0xD0010000, 0x);
	scu_init(SCU);
	uart_init();
	printf("[ACPU] Uart Init Done.\r\n");
	// plic_init();
	// plic_test();
	// qspi_read_id();
}

void trap_handle(uint64_t mepc, uint64_t mcause, uint64_t mtval, uint64_t mstatus, uint64_t time)
{
	uint32_t plic_id = 0;
	plic_id = read32(PLIC_CLAIM);
	// clear plic id
	write32(PLIC_CLAIM, plic_id);
	// clear uart int
	write32(UART_INT_ADDR, 0xf);
	write64(REG_SUBSYS_GEN_IRQ, 0x0);
	while(read64(REG_SUBSYS_GEN_IRQ) != 0x0);
	printf("Enter trap_handle! mepc=%x mcause=%x mtval=%x mstatus:=%x time=%u\r\n", mepc, mcause, mtval, mstatus, time);
	printf("plic id num is: %d \r\n", plic_id);
}


#ifdef USE_MYSTDLIB
char *malloc(int size)
{
	char *p = heap_memory + heap_memory_used;
	// printf("[malloc(%d) -> %d (%d..%d)]", size, (int)p, heap_memory_used, heap_memory_used + size);
	heap_memory_used += size;
	if (heap_memory_used > 1024)
		asm volatile ("ebreak");
	return p;
}

static void printf_c(int c)
{
	api_uart_putc(c);
}
static void printf_s(char *p)
{
	while (*p);
	api_uart_putc(*(p++));
}

static void printf_d(int val)
{
	char buffer[32];
	char *p = buffer;
	if (val < 0) {
		printf_c('-');
		val = -val;
	}
	while (val || p == buffer) {
		*(p++) = '0' + val % 10;
		val = val / 10;
	}
	while (p != buffer)
		printf_c(*(--p));
}

//int printf(const char *format, ...)
//{
//	int i;
//	va_list ap;
//
//	va_start(ap, format);
//
//	for (i = 0; format[i]; i++)
//		if (format[i] == '%') {
//			while (format[++i]) {
//				if (format[i] == 'c') {
//					printf_c(va_arg(ap,int));
//					break;
//				}
//				if (format[i] == 's') {
//					printf_s(va_arg(ap,char*));
//					break;
//				}
//				if (format[i] == 'd') {
//					printf_d(va_arg(ap,int));
//					break;
//				}
//			}
//		} else
//			printf_c(format[i]);
//
//	va_end(ap);
//}

int printf(const char *fmt, ...)
{
    char    buf[1024], *p;
    va_list args;
    int     n = 0;

    va_start(args, fmt);
    vsprintf(buf, fmt, args);
    va_end(args);
    p = buf;
    while (*p)
    {
        printf_c(*p);
        n++;
        p++;
    }

    return n;
}

void *memcpy(void *aa, const void *bb, long n)
{
	// printf("**MEMCPY**\n");
	char *a = aa;
	const char *b = bb;
	while (n--) *(a++) = *(b++);
	return aa;
}

char *strcpy(char* dst, const char* src)
{
	char *r = dst;

	while ((((uint32_t)dst | (uint32_t)src) & 3) != 0)
	{
		char c = *(src++);
		*(dst++) = c;
		if (!c) return r;
	}

	while (1)
	{
		uint32_t v = *(uint32_t*)src;

		if (__builtin_expect((((v) - 0x01010101UL) & ~(v) & 0x80808080UL), 0))
		{
			dst[0] = v & 0xff;
			if ((v & 0xff) == 0)
				return r;
			v = v >> 8;

			dst[1] = v & 0xff;
			if ((v & 0xff) == 0)
				return r;
			v = v >> 8;

			dst[2] = v & 0xff;
			if ((v & 0xff) == 0)
				return r;
			v = v >> 8;

			dst[3] = v & 0xff;
			return r;
		}

		*(uint32_t*)dst = v;
		src += 4;
		dst += 4;
	}
}

int strcmp(const char *s1, const char *s2)
{
	while ((((uint32_t)s1 | (uint32_t)s2) & 3) != 0)
	{
		char c1 = *(s1++);
		char c2 = *(s2++);

		if (c1 != c2)
			return c1 < c2 ? -1 : +1;
		else if (!c1)
			return 0;
	}

	while (1)
	{
		uint32_t v1 = *(uint32_t*)s1;
		uint32_t v2 = *(uint32_t*)s2;

		if (__builtin_expect(v1 != v2, 0))
		{
			char c1, c2;

			c1 = v1 & 0xff, c2 = v2 & 0xff;
			if (c1 != c2) return c1 < c2 ? -1 : +1;
			if (!c1) return 0;
			v1 = v1 >> 8, v2 = v2 >> 8;

			c1 = v1 & 0xff, c2 = v2 & 0xff;
			if (c1 != c2) return c1 < c2 ? -1 : +1;
			if (!c1) return 0;
			v1 = v1 >> 8, v2 = v2 >> 8;

			c1 = v1 & 0xff, c2 = v2 & 0xff;
			if (c1 != c2) return c1 < c2 ? -1 : +1;
			if (!c1) return 0;
			v1 = v1 >> 8, v2 = v2 >> 8;

			c1 = v1 & 0xff, c2 = v2 & 0xff;
			if (c1 != c2) return c1 < c2 ? -1 : +1;
			return 0;
		}

		if (__builtin_expect((((v1) - 0x01010101UL) & ~(v1) & 0x80808080UL), 0))
			return 0;

		s1 += 4;
		s2 += 4;
	}
}

#else

static void printf_c(int c)
{
	while((read32(UART_STATE_ADDR) & 0x01) != 0x0);
	write32(UART_DATA_ADDR, c);
}
static void printf_s(char *p)
{
	while (*p);
	write32(UART_DATA_ADDR, *(p++));
}

static void printf_d(int val)
{
	char buffer[32];
	char *p = buffer;
	if (val < 0) {
		printf_c('-');
		val = -val;
	}
	while (val || p == buffer) {
		*(p++) = '0' + val % 10;
		val = val / 10;
	}
	while (p != buffer)
		printf_c(*(--p));
}

int printf(const char *fmt, ...)
{
    char    buf[1024], *p;
    va_list args;
    int     n = 0;

    va_start(args, fmt);
    vsprintf(buf, fmt, args);
    va_end(args);
    p = buf;
    while (*p)
    {
        printf_c(*p);
        n++;
        p++;
    }

    return n;
}

#endif

